#include <ctime>
#include<graphics.h>
#include<deque>
#include<algorithm>
#include <fstream>

const int WIDTH = 640;
const int HEIGHT = 480;
const int BLOCK_SIZE = 20;
const int ROWS = HEIGHT / BLOCK_SIZE;
const int COLS = WIDTH / BLOCK_SIZE;       //游戏坐标范围 x:[0,COLS - 1]  y:[0,ROWS - 1]
const int SPEED = 10;    // 每秒移动SPEED格
const int FPS = 120;

struct Point {
    int x,y;
    bool operator==(const Point & other) const{
        return other.x == x && other.y == y;
    }
    Point(int x,int y) : x(x), y(y) {}
};
enum Direction {UP, DOWN, LEFT, RIGHT};
enum GameState {MENU,RUNNING,OVER,EXIT};

class SnakeGame {
public:
    void Game() {
        init();
        while (true) {
            if (state == MENU) {
                menu();
            }
            else if (state == RUNNING) {
                running();
            }
            else if (state == OVER) {
                over();
            }
            else if (state == EXIT) {
                exit();
                return;
            }
        }
    }

private:
    const char* SCORE_FILE = "best_score.txt";
    std::deque<Point> snake;
    Point food = Point(0, 0);
    Direction dir;
    clock_t lastMove = clock();
    clock_t lastDraw = clock();
    int score = 0;
    int bestScore = 0;
    GameState state = MENU;

    void running() {
        snake.clear();
        dir = RIGHT;
        snake.push_front(Point(COLS/2,ROWS/2));
        snake.push_back(Point(COLS/2 - 1,ROWS/2));
        snake.push_back(Point(COLS/2 - 2,ROWS/2));
        score = snake.size();
        generateFood();
        while (state == RUNNING) {
            handleRunningInput();
            move();
            draw();
        }
        EndBatchDraw();
    }

    void menu() {
        cleardevice();
        while (state == MENU) {
            handleMenuInput();

            setbkmode(TRANSPARENT);
            settextcolor(WHITE);

            settextstyle(48, 0, _T("Arial"));
            const TCHAR* title = _T("Snake Game");
            int titleWidth = textwidth(title);
            int titleHeight = textheight(title);
            outtextxy((WIDTH - titleWidth) / 2, HEIGHT / 2 - titleHeight - 20, title);

            settextstyle(24, 0, _T("Arial"));
            const TCHAR* powered = _T("Powered by Hhhjt");
            int poweredWidth = textwidth(powered);
            outtextxy((WIDTH - poweredWidth) / 2, HEIGHT / 2 + 10, powered);

            settextstyle(20, 0, _T("Arial"));
            const TCHAR* exitText = _T("Press Esc to exit...");
            const TCHAR* restartText = _T("Press Enter to start...");
            int padding = 10;
            outtextxy(WIDTH - textwidth(exitText) - padding, HEIGHT - 2 * textheight(exitText) - padding, exitText);
            outtextxy(WIDTH - textwidth(restartText) - padding, HEIGHT - textheight(restartText) - padding, restartText);
        }
    }


    void over() {
        cleardevice();
        while (state == OVER) {
            handleOverInput();

            setbkmode(TRANSPARENT);
            settextcolor(WHITE);

            settextstyle(40, 0, _T("Arial"));
            const TCHAR* overText = _T("Game Over");
            int overWidth = textwidth(overText);
            int overHeight = textheight(overText);
            int centerY = HEIGHT / 2 - overHeight / 2;
            outtextxy((WIDTH - overWidth) / 2, centerY, overText);

            settextstyle(24, 0, _T("Arial"));
            std::string scoreText = "Score: " + std::to_string(score) + "   Best: " + std::to_string(bestScore);
            int scoreWidth = textwidth(scoreText.c_str());
            outtextxy((WIDTH - scoreWidth) / 2, centerY + overHeight + 20, scoreText.c_str());

            settextstyle(20, 0, _T("Arial"));
            const TCHAR* exitText = _T("Press Esc to exit...");
            const TCHAR* restartText = _T("Press Enter to restart...");
            int padding = 10;
            outtextxy(WIDTH - textwidth(exitText) - padding, HEIGHT - 2 * textheight(exitText) - padding, exitText);
            outtextxy(WIDTH - textwidth(restartText) - padding, HEIGHT - textheight(restartText) - padding, restartText);
        }
    }

    void exit() {
        saveBestScore();
    }

    void loadBestScore() {
        std::ifstream fin(SCORE_FILE);
        if (fin.is_open()) {
            fin >> bestScore;
            fin.close();
        }
    }

    void saveBestScore() {
        std::ofstream fout(SCORE_FILE);
        if (fout.is_open()) {
            fout << bestScore;
            fout.close();
        }
    }


    void init() {
        initgraph(WIDTH, HEIGHT);
        setbkcolor(BLACK);   // 设置背景颜色
        loadBestScore();
    }

    void draw() {
        clock_t now = clock();
        double deltaTime = (double)(now - lastDraw) / CLOCKS_PER_SEC;
        BeginBatchDraw();     // 双缓冲
        if (deltaTime * FPS >= 1) {
            cleardevice();
            drawScore();
            drawSnake();
            drawFood();
            lastDraw = now;
        }
        FlushBatchDraw();
    }

    void drawSnake() {
        setfillcolor(GREEN);   // 设置Snake颜色
        for (auto p : snake) {
            solidrectangle(p.x * BLOCK_SIZE, p.y * BLOCK_SIZE,p.x * BLOCK_SIZE + BLOCK_SIZE,p.y * BLOCK_SIZE + BLOCK_SIZE);
        }
    }

    void drawFood() {
        setfillcolor(RED);
        solidrectangle(food.x * BLOCK_SIZE,food.y * BLOCK_SIZE,food.x * BLOCK_SIZE + BLOCK_SIZE,food.y * BLOCK_SIZE + BLOCK_SIZE);
    }

    void drawScore() {
        LOGFONT f;
        gettextstyle(&f);                      // 获取当前字体设置
        f.lfHeight = 24;                       // 字体高度
        strcpy(f.lfFaceName, "Arial");
        f.lfQuality = ANTIALIASED_QUALITY;     // 抗锯齿
        settextstyle(&f);                      // 应用字体设置

        setbkmode(TRANSPARENT);                // 设置背景透明
        settextcolor(WHITE);                   // 设置字体颜色

        std::string scoreText = "Score:" + std::to_string(score);
        outtextxy(0, 0, scoreText.c_str());

        std::string bestScoreText = "BestScore:" + std::to_string(bestScore);
        outtextxy(0, 24, bestScoreText.c_str());

    }

    void move() {
        clock_t now = clock();
        double deltaTime = (double)(now - lastMove) / CLOCKS_PER_SEC;
        if (deltaTime * SPEED >= 1) {
            Point head = snake.front();
            switch (dir) {
                case UP: head.y--; break;
                case DOWN: head.y++; break;
                case LEFT: head.x--; break;
                case RIGHT: head.x++; break;
            }
            head.x = (head.x + COLS) % COLS;
            head.y = (head.y + ROWS) % ROWS;

            if (std::find(snake.begin(), snake.end(), head) != snake.end()) {
                state = OVER;
            }

            snake.push_front(head);
            if (head == food) {
                generateFood();
                score = snake.size();
                bestScore = std::max(score, bestScore);
            }
            else {
                snake.pop_back();
            }


            lastMove = now;
        }
    }

    void handleRunningInput() {
        ExMessage msg;
        while (peekmessage(&msg, EM_KEY)) {
            if (msg.message != WM_KEYDOWN) continue;  // 只处理“按下”事件

            switch (msg.vkcode) {
                case VK_UP:    if (dir != DOWN)  dir = UP;    break;
                case VK_DOWN:  if (dir != UP)    dir = DOWN;  break;
                case VK_LEFT:  if (dir != RIGHT) dir = LEFT;  break;
                case VK_RIGHT: if (dir != LEFT)  dir = RIGHT; break;
                case VK_ESCAPE:
                    snake.clear();
                if (score > bestScore) bestScore = score;
                state = OVER;
                break;
            }
        }
    }

    void handleMenuInput() {
        ExMessage msg;
        while (peekmessage(&msg, EM_KEY)) {
            if (msg.message == WM_KEYDOWN) {
                switch (msg.vkcode) {
                    case VK_ESCAPE: state = EXIT; break;
                    case VK_RETURN: state = RUNNING; break;
                }
            }
        }
    }

    void handleOverInput() {
        ExMessage msg;
        while (peekmessage(&msg, EM_KEY)) {
            if (msg.message == WM_KEYDOWN) {
                switch (msg.vkcode) {
                    case VK_ESCAPE: state = EXIT; break;
                    case VK_RETURN: state = RUNNING; break;
                }
            }
        }
    }

    void generateFood() {
        srand((unsigned)time(nullptr));
        while (true) {
            int x = rand() % COLS;
            int y = rand() % ROWS;
            Point newFood = Point(x,y);
            if (std::find(snake.begin(), snake.end(), newFood) == snake.end()) {
                food = newFood;
                return;
            }
        }
    }

};

int main() {
    SnakeGame Game;
    Game.Game();
}

